package.path = package.path..";./lib/?.lua"

local rs232 = require"luars232"

local task = require"task"

local lfs = require"lfs"

require"base"

require"taskmsg"

require"atcmd"

-----class atworker
local catworker={}

function catworker:new(o)
    o = o or {}
    
    --set class
    setmetatable(o, self)
    self.__index = self
    
    return o
end

--currently this function only works in win32/linux,but linux only tested
--in ubuntu
function catworker:detect_port()
    local port_name_prefix,port_num_begin
    --if "c:/" exists,then it is win32
    if (lfs.attributes("c:/")~=nil) then
        --In windows,if the com port num>9,it should use UNC name of the device
        --see http://github.com/ynezz/librs232/issues/closed#issue/1
        port_name_prefix = "\\\\.\\COM"
        port_num_begin = 1
    --if /dev exists,then it might be in linux
    elseif (lfs.attributes("/dev")~=nil) then
        port_name_prefix = "/dev/ttyACM"
        port_num_begin = 0
    else--I don't know how to detect,what os it is? u can add it youself...
        return
    end
    --print(port_name_prefix)
    
    local i,port_name
    for i=port_num_begin,30,1 do
        port_name = port_name_prefix..i
        local e, p = rs232.open(port_name)
        --print(port_name,e,p)
        if (e == rs232.RS232_ERR_NOERROR and p~=nil)  then
            --print(port_name)
            p:write(atcmd.AT_ECHO_OFF(),300)
            local e,r,n = p:read(100,300)
            if r then
                if string.find(r,"OK")~=nil then
                    self.p = p
                    print(self.p)
                    return port_name
                end
            end
        end
    end
end

function catworker:write_at_cmd(at_cmd,timeout)
    local e, len_written,l
    
    assert(type(at_cmd)=="string")
    if (at_cmd == "") then return end
    
    len_written = 0
    --in some of my phone,if send long string to phone
    --the com port can not be used any more until phone replug
    --and I try if I send 3 byte once,it will be ok
    --this issue only happen in win32,not in linux
    local t = at_cmd:len()/3+1
    local s
    for i = 1, t do
        s = at_cmd:sub((i-1)*3+1,i*3)
        if s~="" then
            e,l = self.p:write(s)
            len_written = len_written+l
        else
            break
        end
        if (e ~= rs232.RS232_ERR_NOERROR) then
            break
        end
    end
    --print(string.format("write %d bytes,err:%d",len_written,e))
    return e,len_written
end

function catworker:read(timeout)
    local e
    local r=""
    local n=0
    local zero_count = 0,data,bytes
    --in timeout time,if read some data,and continue not read any
    --bytes for 6x200 ms,we consider read finished
    for i = timeout,200,-200 do
        e,data,bytes = self.p:read(4000,200)
        --print(string.format("read %d bytes,err:%d,i=%d",bytes,e,i))
        if data~=nil then r = r..data end
        n = n + bytes
        if bytes==0 then zero_count = zero_count +1 end
        if (n>0 and zero_count>=6) then break end
        if e~=rs232.RS232_ERR_NOERROR then break end
        --that is according my thinking:if there is "OK\r",that we can
        --make sure that AT cmd have successfully execute and finished read
        --result.But may be there is some exceptions!
        if (data~=nil and data:find("OK\r")~=nil) then break end
    end
    return e,r,n
end

function catworker:do_at_cmd(at_cmd,timeout)
    local timeout = timeout or 800 --200 is a time unit
    local e,len_written,r,n,i
    if at_cmd ~= "AT\r" then
        print("do_at_cmd",atcmd.repr(at_cmd))
    end
    if self.p~=nil then
        for i = 1,3 do --now in fact we do not use resend
            e, len_written = self:write_at_cmd(at_cmd,timeout)
            --assert(len_written>0)
            --print(string.format("rs232 write %d bytes",len_written))
            if(e ~= rs232.RS232_ERR_NOERROR) then
                r = nil
                break
            end
            
            e,r,n = self:read(timeout)
            print(string.format("rs232 write %d bytes,read %d bytes",len_written,n))
            assert(e == rs232.RS232_ERR_NOERROR)
            if at_cmd ~= "AT\r" then
                print("do_at_cmd return:",atcmd.repr(r))
            end
            if r~=nil then break end
            --print("retry......")
            --now not using resend,so do not allow to run to here
            break
        end
        
        --if there is no return at result,I think it means that phone not connected
        if r==nil or r=="" then
            print("no result return,so close the port")
            assert(self.p:close() == rs232.RS232_ERR_NOERROR)
            self.p = nil
        end
        
        return r
    else
        print("there is no port open...")
    end
end
-------------------

local function main()
    --print("atworker begin...")
    --register name 'atworker'
    local rc = task.register('atworker')
    assert(rc==0)
    
    --get main id
    local id_main = task.find('main')
    assert(id_main>0)
    print("id_main",id_main)
    
    local atworker = catworker:new()
    
    --main loop
    local msg, flags, rc
    local current_rs232_port
    local pause_detector
    while true do
        msg = taskmsg.receive()
        if (msg.at_type ~= "AT") then
            print("atworker receive:",atcmd.repr(msg))
        end
        if (msg.msgtype=="SEARCH_CONNECTED_COM_REQ") then
            local msg2back=
            {
                msgtype = "SEARCH_CONNECTED_COM_CNF",
                port = atworker:detect_port()
            }
            print("send back",msg2back)
            assert(tonumber(msg.from)~=nil)
            taskmsg.post(msg.from,msg2back)
        elseif (msg.msgtype=="AT_CMD_REQ") then
            --remember the pause_detector setting
            if msg.pause_detector ~= nil then
                pause_detector = msg.pause_detector
            end
            --print(pause_detector,msg.at_type)
            --if pause_detector,then we ignore the "AT\n"
            if not (pause_detector and msg.at_type=="AT") then
                local msg2back=
                {
                    msgtype = "AT_CMD_CNF",
                    at_type = msg.at_type,
                    reply = atworker:do_at_cmd(msg.at_cmd,msg.timeout),
                }
                assert(tonumber(msg.from)~=nil)
                rc = taskmsg.post(msg.from,msg2back)
                if (msg.at_type ~= "AT") then
                    print("send back",msg.from,rc,atcmd.repr(msg2back))
                end
            else
                print("pause_detector,so we ignore the AT cmd")
            end
            
        end
    end
end

main()